# 克隆自聚宽文章：https://www.joinquant.com/post/9578
# 标题：【新手入门教程】市值轮动策略2.0
# 作者：JoinQuant-TWist

# 初始化
def initialize(context):
    g.stocksnum = 5 # 持有最小市值股票数
    g.period = 10 # 轮动频率
    run_daily(daily,time='every_bar')# 周期循环daily
    g.days = 1 # 记录策略进行到第几天，初始为1
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
# 周期循环
def daily(context):
    # 每日止损
    stop(context)
    # 判断策略进行天数是否能被轮动频率整除余1
    if g.days % g.period == 1:
        # 选出要交易的股票
        buylist=pick(context)
        # 交易下单
        trade(context,buylist)
    else:
        pass # 什么也不做

    g.days = g.days + 1 # 策略经过天数增加1
    
# 止损
def stop(context):
    # 循环查看持仓的每个股票
    for stock in context.portfolio.positions:
        # 如果股票最新价格除以平均成本小于0.8，即亏损超过20%
        if context.portfolio.positions[stock].price/context.portfolio.positions[stock].avg_cost < 0.8: 
            # 调整stock的持仓为0，即卖出
            order_target(stock, 0) 
            # 输出日志：股票名 止损
            print "\n%s 止损" % stock

# 选股
def pick(context):
    # 获取当前时间
    date=context.current_dt.strftime("%Y-%m-%d")
    # 获取上证指数和深证综指的成分股代码并连接，即为全A股市场所有股票
    scu = get_index_stocks('000001.XSHG')+get_index_stocks('399106.XSHE')

    # 选出在scu内的股票的股票代码，并按照当前时间市值从小到大排序
    df = get_fundamentals(query(
            valuation.code,valuation.market_cap
        ).filter(
            valuation.code.in_(scu)
        ).order_by(
            valuation.market_cap.asc()
        ), date=date
        )

    # 取出前g.stocksnum名的股票代码，并转成list类型，buylist为选中的股票
    # 这里是先选再过滤，可能导致最终选出的数量<g.stocksnum
    buylist =list(df['code'][:g.stocksnum])
    # 过滤停牌股票
    buylist=paused_filter(buylist)
    # 过滤涨停股票
    buylist=high_limit_filter(context,buylist)
    # 过滤st股票
    buylist=st_filter(buylist)
    return buylist

# 交易
def trade(context,buylist):
    # 对于每个当下持有的股票进行判断：现在是否已经不在buylist里，如果是则卖出
    for stock in context.portfolio.positions:
        if stock not in buylist: #如果stock不在buylist
            order_target(stock, 0) #调整stock的持仓为0，即卖出

    # 将总资产(现金+股票)除以持股数g.stocksnum
    position_per_stk = context.portfolio.total_value/g.stocksnum
    # 调整buylist中的每个股票持仓价值为position_per_stk
    for stock in buylist:
        order_target_value(stock, position_per_stk)

# 过滤停牌股票
# https://www.joinquant.com/algorithm/apishare/get?apiId=20
def paused_filter(security_list):
    current_data = get_current_data()
    security_list = [stock for stock in security_list if not current_data[stock].paused]
    # 返回结果
    return security_list

# 过滤涨停股票
# https://www.joinquant.com/algorithm/apishare/get?apiId=24
def high_limit_filter(context, security_list):
    current_data = get_current_data()
    security_list = [stock for stock in security_list if not (current_data[stock].day_open>current_data[stock].high_limit*0.985)]
    # 返回结果
    return security_list

# 过滤ST股票
# https://www.joinquant.com/algorithm/apishare/get?apiId=22
def st_filter(security_list):
    current_data = get_current_data()
    security_list = [stock for stock in security_list if not current_data[stock].is_st]
    # 返回结果
    return security_list
